#pragma once
#include <iostream>
#include <vector>
#include <unordered_map>
#include <mutex>
#include <memory>

#include "../common/net.hpp"

namespace RPC
{
    namespace Server
    {
        class ProviderManager
        {
        public:
            using Ptr = std::shared_ptr<ProviderManager>;
            struct Provider
            {
                using Ptr = std::shared_ptr<Provider>;
                // 提供方的基本信息
                std::mutex _mutex;
                Address _host;
                std::vector<std::string> _methods;
                BaseConnection::Ptr _conn;

                Provider(const Address &host, const BaseConnection::Ptr &conn)
                    : _host(host), _conn(conn) {}

                bool operator=(const Provider &other) const
                {
                    return _host == other._host;
                }

                // 提供管理
                void appendMethod(const std::string &method)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    {
                        _methods.emplace_back(method);
                    }
                }
            };

            // 服务上线
            void addProvider(const BaseConnection::Ptr &conn, Address addr, const std::string &method)
            {
                Provider::Ptr prov;
                std::unique_lock<std::mutex> lock(_mutex);
                {
                    // 1、注册方法到_methods
                    auto it = _conn.find(conn);
                    if (it == _conn.end())
                    {
                        prov = std::make_shared<Provider>(addr, conn);
                        _conn.insert({conn, prov});
                    }
                    else
                    {
                        prov = it->second;
                    }
                    // 2、找到对应的方法，加上方法提供者
                    _providers[method].push_back(prov);
                }
                // 3、向provider中加入方法
                prov->appendMethod(method);
            }

            // 获取客服端信息
            Provider::Ptr getProvider(const BaseConnection::Ptr &conn)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _conn.find(conn);
                if (it == _conn.end())
                {
                    return Provider::Ptr();
                }
                return it->second;
            }

            // 客服提供端下线
            void delProvider(const BaseConnection::Ptr &conn)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                // 1、取得对应的provider对象，先删除连接到提供者数据
                Provider::Ptr prov = getProvider(conn);
                if (conn == nullptr)
                {
                    INFO_LOG("当前客服端不是服务的提供者！");
                    return;
                }
                _conn.erase(conn);
                // 2、去对应的方法中删除该提供者的数据
                for (const std::string &method : prov->_methods)
                {
                    _providers[method].erase(std::remove(_providers[method].begin(), _providers[method].end(), prov),
                                             _providers[method].end());
                }
                // 3、当没有智能指针指向provider自动释放
            }

            std::vector<Address> providers(const std::string &method)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                std::vector<Address> providerable;
                std::vector<Provider::Ptr> providers_set = _providers[method];
                if (providers_set.empty())
                {
                    INFO_LOG("暂时未提供对应的方法！");
                    return std::vector<Address>();
                }
                for (const auto &prov : providers_set)
                {
                    providerable.emplace_back(prov->_host);
                }
                // ERROR_LOG("完成了获取服务提供者！");
                return providerable;
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, std::vector<Provider::Ptr>> _providers; // 方法到提供者，进行服务提供
            std::unordered_map<BaseConnection::Ptr, Provider::Ptr> _conn;           // 连接到提供者
        };

        class DiscoverManager
        {
        public:
            using Ptr = std::shared_ptr<DiscoverManager>;
            struct Discover
            {
                using Ptr = std::shared_ptr<Discover>;
                // 提供发现方的基本信息
                BaseConnection::Ptr _conn;
                std::vector<std::string> _method;
                std::mutex _mutex;
                Discover(const BaseConnection::Ptr &conn) : _conn(conn)
                {
                }
                void append(const std::string &method)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _method.emplace_back(method);
                }
            };

            // 服务发现添加方法
            void addDiscover(const BaseConnection::Ptr &conn, const std::string &method)
            {
                Discover::Ptr discover;
                std::unique_lock<std::mutex> lock(_mutex);
                {
                    // 1、添加连接到_conns里面
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        discover = std::make_shared<Discover>(conn);
                        _conns.insert({conn, discover});
                        // 2、添加到_discovers里面
                        _discovers[method].emplace_back(discover);
                    }
                    else
                    {
                        discover = it->second;
                    }
                }
                // 3、添加到Discover的方法里面
                discover->append(method);
            }

            // 发现者下线删除
            void delDiscover(const BaseConnection::Ptr &conn)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                {
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        INFO_LOG("当前的下线不是发现者!");
                        return;
                    }
                    // 1、删除_conns对应关系
                    _conns.erase(it);
                    // 2、在方法与发现者的映射中删除该发现者
                    for (auto method : it->second->_method)
                    {
                        _discovers[method].erase(std::remove(_discovers[method].begin(), _discovers[method].end(), it->second),
                                                 _discovers[method].end());
                    }
                    // 3、discover自动销毁。
                }
            }

            // 上线通知
            void onlineNoticy(const std::string &method, const Address &host)
            {
                return noticy(method, host, ServiceOpType::SERVICE_ONLINE);
            }

            // 下线通知  ?怎么知道这个host提供了哪些方法
            void offlineNoticy(const std::string &method, const Address &host)
            {
                return noticy(method, host, ServiceOpType::SERVICE_OFFLINE);
            }

        private:
            // 给发现过该方法的用户发送消息
            void noticy(const std::string &method, Address host, ServiceOpType type)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                // ERROR_LOG("正在运行！%s", host.first.c_str());
                // ERROR_LOG("正在运行！%d", host.second);
                auto it = _discovers.find(method);
                if (it == _discovers.end())
                {
                    // 说明当前方法还没有被发现过.
                    return;
                }
                // ERROR_LOG("当前服务被发现过！");
                ServiceRequest::Ptr srequest = MessageFactory::create<ServiceRequest>();
                srequest->setHost(host);
                srequest->setId(UUID::uuid());
                srequest->setMethod(method);
                srequest->setOpType(type);
                srequest->setType(MType::REQ_SERVICE);
                // if(it->second.empty())
                // {
                //     ERROR_LOG("当前方法被发现过，但是发现者队列为空!");
                // }
                for (auto discover : it->second)
                {
                    INFO_LOG("向客服端发送断开请求。")
                    discover->_conn->send(srequest);
                }
            }
            std::mutex _mutex;
            std::unordered_map<std::string, std::vector<Discover::Ptr>> _discovers;
            std::unordered_map<BaseConnection::Ptr, Discover::Ptr> _conns;
        };

        class PDManager
        {
        public:
            using Ptr = std::shared_ptr<PDManager>;

            PDManager()
                : _discovermanager(std::make_shared<DiscoverManager>()), _providermanager(std::make_shared<ProviderManager>())
            {
            }

            // 客服端向 （注册中心进行服务发现时/服务提供者向注册中心组测业务）进行的回调函数
            void onServerRequest(const BaseConnection::Ptr &conn, const ServiceRequest::Ptr &msg)
            {
                ServiceOpType optype = msg->opType();
                std::string method = msg->method();
                if (optype == ServiceOpType::SERVICE_REGISTER) // 服务注册 （服务提供端）
                {
                    // 1、注册服务
                    _providermanager->addProvider(conn, msg->host(), method);
                    // 2 、给发现过该服务的 客服注册端 提供消息发送。
                    _discovermanager->onlineNoticy(method, msg->host());
                    // 3、给注册服务的 客服用户端 提供应答
                    return registerResponse(conn, msg);
                }
                else if (optype == ServiceOpType::SERVICE_DISCOVERY) // 服务发现 （客服发现端）
                {
                    // 1、去提供服务端获得对应的服务
                    std::vector<Address> providers = _providermanager->providers(method);
                    // 2、新增服务发现者
                    _discovermanager->addDiscover(conn, msg->method());
                    // 3、发消息给当前需要服务的（客服用户端）
                    if (providers.empty() == true)
                    {
                        ERROR_LOG("当前注册中心搜素不到该方法！");
                        return blankResponse(conn, msg);
                    }
                    return discoverResponse(conn, msg, providers);
                }
                else
                {
                    ERROR_LOG("收到服务请求，但是类型转化错误!");
                    // 1、发消息给连接，未找到该服务类型。
                    return errorResponse(conn, msg);
                }
            }

            // 提供服务下线 这个回调函数提供给注册中心连接断开前调用
            void onConnShutdown(const BaseConnection::Ptr &conn)
            {
                ERROR_LOG("中转中心准备向客服端发起和服务提供者的断开消息。")
                ProviderManager::Provider::Ptr provider = _providermanager->getProvider(conn);
                if (provider != nullptr)
                {

                    for (const auto &method : provider->_methods)
                    {
                        _discovermanager->offlineNoticy(method, provider->_host);
                    }
                    // 需要删除服务提供者信息并下线通知
                    _providermanager->delProvider(conn);
                }
                // 当前不是服务提供者，因此删除发现记录后退出
                _discovermanager->delDiscover(conn);
            }

        private:
            void errorResponse(const BaseConnection::Ptr &conn, const ServiceRequest::Ptr &msg)
            {
                ServiceResponse::Ptr resp = MessageFactory::create<ServiceResponse>();
                resp->setId(msg->id());
                resp->setOpType(ServiceOpType::SERVICE_UNKNOW);
                resp->setRCode(RCode::RCODE_INVAILD_SERVICE_OPE);
                resp->setType(MType::RES_SERVICE);
                return conn->send(resp);
            }

            void blankResponse(const BaseConnection::Ptr &conn, const ServiceRequest::Ptr &msg)
            {
                ServiceResponse::Ptr resp = MessageFactory::create<ServiceResponse>();
                resp->setId(msg->id());
                resp->setOpType(ServiceOpType::SERVICE_DISCOVERY);
                resp->setRCode(RCode::RCODE_NOT_FOUND_RPC);
                resp->setType(MType::RES_SERVICE);
                return conn->send(resp);
            }

            void registerResponse(const BaseConnection::Ptr &conn, const ServiceRequest::Ptr &msg)
            {
                ServiceResponse::Ptr resp = MessageFactory::create<ServiceResponse>();
                resp->setId(msg->id());
                resp->setOpType(ServiceOpType::SERVICE_REGISTER);
                resp->setRCode(RCode::RCODE_OK);
                resp->setType(MType::RES_SERVICE);
                return conn->send(resp);
            }

            void discoverResponse(const BaseConnection::Ptr &conn, const ServiceRequest::Ptr &msg,
                                  const std::vector<Address> &providers)
            {
                ServiceResponse::Ptr resp = MessageFactory::create<ServiceResponse>();
                resp->setId(msg->id());
                resp->setOpType(ServiceOpType::SERVICE_DISCOVERY);
                resp->setType(MType::RES_SERVICE);
                if (providers.empty() == true)
                {
                    resp->setRCode(RCode::RCODE_NOT_FOUND_SERVICE);
                    return conn->send(resp);
                }
                resp->setRCode(RCode::RCODE_OK);
                resp->setHost(providers);
                resp->setMethod(msg->method());
                return conn->send(resp);
            }

        private:
            DiscoverManager::Ptr _discovermanager;
            ProviderManager::Ptr _providermanager;
        };
    }

}